#include "redis_cpp.h"

#include <hiredis/hiredis.h>
#include <string>
#include <string_view>
#include <charconv>
using namespace std;

#include <spdlog/spdlog.h>

RedisCpp::RedisCpp(const RedisLogin &login_info)
    : login_info_(login_info)
{
    context_ = redisConnect(login_info.host.data(), login_info.port);
    if (context_ && context_->err) {
        throw context_->errstr;
    }
    if (login_info.passwd.size()) {
        ReplyPtr reply_ptr_ = redisCommand(context_, "auth %s", login_info.passwd.c_str());
        std::cout << "login...\n";
        if (reply_ptr_) {
            std::cerr << reply_ptr_->str << std::endl;
        }
        else {
            throw "login error\n";
        }
    }
    select(login_info.db_index);
}

RedisCpp::RedisCpp(std::string_view host, uint16_t port, int index, const std::string &username, const std::string &passwd)
    : RedisCpp(RedisLogin{.host = host.data(), .port = port, .db_index = index, .username=username, .passwd=passwd})
{
}

RedisCpp::~RedisCpp()
{
    redisFree(context_);
}

bool RedisCpp::select(int index)
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "select %d", index);
    return reply_ptr_;
}

bool RedisCpp::set(std::string_view key, int v)
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "set %s %d", key.data(), v);
    return reply_ptr_;
}

bool RedisCpp::set(std::string_view key, const std::string &v)
{
    return set(key, string_view(v.c_str()));
}

bool RedisCpp::set(std::string_view key, std::string_view v)
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "set %s %s", key.data(), v.data());
    if (!reply_ptr_) {
        throw "set key error";
    }
    if (!reply_ptr_->str) {
        std::cerr << reply_ptr_->str << std::endl;
    }
    return reply_ptr_;
}

bool RedisCpp::set(std::string_view key, double v)
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "set %s %s", key.data(), to_string(v).c_str());
    return reply_ptr_;
}

bool RedisCpp::set(std::string_view key, boost::gregorian::date v)
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "set %s %s", key.data(), boost::gregorian::to_iso_extended_string(v).c_str());
    return reply_ptr_;
}

template <>
int RedisCpp::get<int>(std::string_view key) const
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "GET %s", key.data());
    return atoi(reply_ptr_->str);
}

template <>
std::string RedisCpp::get<std::string>(std::string_view key) const
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "GET %s", key.data());
    return reply_ptr_->str ? reply_ptr_->str : "";
}

template <>
double RedisCpp::get<double>(std::string_view key) const
{
    assert(false);
    ReplyPtr reply_ptr_ = redisCommand(context_, "GET %s", key.data());
    double d;
    std::from_chars(reply_ptr_->str, reply_ptr_->str + strlen(reply_ptr_->str), d);
    return d;
}

template <>
boost::gregorian::date RedisCpp::get<boost::gregorian::date>(std::string_view key) const
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "GET %s", key.data());
    return reply_ptr_->str ? boost::gregorian::from_simple_string(reply_ptr_->str) : boost::gregorian::date();
}

bool RedisCpp::flushdb()
{
    ReplyPtr reply_ptr_ = redisCommand(context_, "flushdb");
    return reply_ptr_;
}
